<template>
  <view class="chat-container">
    <!-- 顶部导航 -->
    <view class="nav-header">
      <view class="nav-back" @click="goBack">
        <text class="back-icon">←</text>
      </view>
      <text class="nav-title">{{ patientName || '杨蒙' }}</text>
      <view class="nav-right"></view>
    </view>

    <!-- 聊天内容区域 -->
    <scroll-view
      class="chat-content"
      :style="{ bottom: contentBottom + 'rpx' }"
      scroll-y="true"
      :scroll-top="scrollTop"
      scroll-with-animation="true"
      :scroll-into-view="toView"
    >
      <!-- 动态消息列表 -->
      <view v-for="(message, index) in messages" :key="message.id" :id="'m_'+message.id">
        <!-- 时间显示 -->
        <view class="time-divider" v-if="shouldShowTime(index)">
          <text class="time-text">{{ message.timestamp }}</text>
        </view>

        <!-- 患者消息（右侧） -->
        <view class="message-item patient-message" v-if="message.sender === 'patient'">
          <view class="message-bubble patient-bubble">
            <!-- 文本消息 -->
            <text class="message-text" v-if="message.type === 'text'">{{ message.content }}</text>
            <!-- 图片消息 -->
            <image
              v-if="message.type === 'image'"
              class="message-image"
              :src="message.extra"
              mode="aspectFit"
              @click="previewImage(message.extra)"
            ></image>
            <!-- 语音消息 -->
            <view v-if="message.type === 'voice'" class="voice-message" @tap="playVoice(message.extra, message.id)">
              <text class="voice-icon">🎤</text>
              <text class="voice-duration">{{ formatVoiceDuration(message.duration) }}</text>
            </view>
          </view>
          <view class="avatar patient-avatar">
            <image class="avatar-img" :src="meAvatarFinal" mode="aspectFill"></image>
          </view>
        </view>

        <!-- 医生消息（左侧） -->
        <view class="message-item doctor-message" v-if="message.sender === 'doctor'">
          <view class="avatar doctor-avatar">
            <image class="avatar-img" :src="doctorAvatarFinal" mode="aspectFill"></image>
          </view>
          <view class="message-bubble doctor-bubble">
            <!-- 文本消息 -->
            <text class="message-text" v-if="message.type === 'text'">{{ message.content }}</text>
            <!-- 图片消息 -->
            <image
              v-if="message.type === 'image'"
              class="message-image"
              :src="message.extra"
              mode="aspectFit"
              @click="previewImage(message.extra)"
            ></image>
            <!-- 语音消息 -->
            <view v-if="message.type === 'voice'" class="voice-message" @tap="playVoice(message.extra, message.id)">
              <text class="voice-icon">🎤</text>
              <text class="voice-duration">{{ formatVoiceDuration(message.duration) }}</text>
            </view>
          </view>
        </view>
        
        <!-- 系统动作消息（结束后操作区） -->
        <view class="system-message" v-if="message.type === 'action'">
          <view class="action-card">
            <text class="action-title">{{ message.content || '本次咨询已结束' }}</text>
            <view class="action-buttons">
              <view class="action-btn primary" @click="restartConsult">重新咨询</view>
              <view class="action-btn" @click="goToComment">去评价</view>
            </view>
          </view>
        </view>
      </view>

      <!-- 默认示例消息（当没有动态消息时显示） -->
      <template v-if="messages.length === 0">
        <!-- 时间显示 -->
        <view class="time-divider">
          <text class="time-text">上午11:10</text>
        </view>

        <!-- 患者消息（右侧） -->
        <view class="message-item patient-message">
          <view class="message-bubble patient-bubble">
            <text class="message-text">上火吃什么药，霍香正气丸有效吗？</text>
          </view>
          <view class="avatar patient-avatar">
            <text class="avatar-text">患</text>
          </view>
        </view>

        <!-- 医生回复（左侧） -->
        <view class="message-item doctor-message">
          <view class="avatar doctor-avatar">
            <image class="avatar-img" src="/static/dayi.png" mode="aspectFill"></image>
          </view>
          <view class="message-bubble doctor-bubble">
            <text class="message-text">可以的，霍香正气丸具有解表化湿，理气和中的作用。常用于暑湿感冒，头痛身重胸闷，或恶寒发热，脘腹胀痛，呕吐泄泻。</text>
          </view>
        </view>
      </template>
      <!-- 底部占位，避免被输入区/面板遮挡 -->
      <view :style="{ height: contentBottom + 'rpx' }"></view>
    </scroll-view>

    <!-- 底部输入区域 -->
    <!-- 输入区域 -->
    <view class="input-area" ref="inputArea" :style="{ bottom: (panelHeight + (keyboardHeightRpx || 0)) + 'rpx' }">
      <!-- 预览选中的图片 -->
      <view class="images-preview" v-if="selectedImages.length > 0">
        <view
          class="image-preview"
          v-for="(image, index) in selectedImages"
          :key="index"
        >
          <image class="preview-img" :src="image" mode="aspectFit"></image>
          <view class="remove-btn" @click="removeSelectedImage(index)">
            <text class="remove-icon">×</text>
          </view>
        </view>
      </view>

      <!-- 输入工具栏 -->
      <view class="input-tools">
      <!-- 语音/键盘切换按钮 -->
      <view class="tool-btn" @click="toggleVoiceMode">
      <text class="tool-icon">{{ isVoiceMode ? '⌨️' : '🎤' }}</text>
      </view>

      <!-- 输入区域：文字输入 或 按住说话 -->
      <view class="input-container">
        <!-- 文本输入模式 -->
        <input
          v-if="!isVoiceMode"
          class="message-input"
          v-model="inputMessage"
          placeholder="请输入消息..."
          @focus="onInputFocus"
          @blur="onInputBlur"
          @confirm="sendMessage"
          confirm-type="send"
        />
        <!-- 语音模式：按住说话按钮 -->
        <view
          v-else
          class="press-to-talk"
          @touchstart="startRecording"
          @touchmove="onRecordTouchMove"
          @touchend="stopRecording"
          @touchcancel="cancelRecording"
        >
          <text class="press-text">按住 说话</text>
        </view>
      </view>

      <!-- 表情按钮 -->
      <view class="tool-btn" @click="toggleEmojiPanel">
      <text class="tool-icon">😊</text>
      </view>

      <!-- 发送按钮 -->
      <view class="send-btn" @click="sendMessage" :class="{active: canSend}">
        <text class="send-text">发送</text>
      </view>

      <!-- 加号按钮（更多功能） -->
      <view class="tool-btn" @click="toggleMorePanel">
        <text class="tool-icon" :class="{rotated: showMorePanel}">+</text>
      </view>
      </view>

      <!-- 表情面板 -->
      <view v-if="showEmojiPanel" class="emoji-panel">
        <view class="emoji-grid">
          <view
            class="emoji-item"
            v-for="emoji in emojiList"
            :key="emoji"
            @click="selectEmoji(emoji)"
          >
            <text class="emoji-text">{{ emoji }}</text>
          </view>
        </view>
      </view>

      <!-- 更多功能面板 -->
      <view v-if="showMorePanel" class="more-panel">
        <view class="more-grid">
          <!-- 图片功能 -->
          <view class="more-item" @click="chooseImage">
            <view class="more-icon-bg photo">
              <text class="more-icon">📷</text>
            </view>
            <text class="more-label">图片</text>
          </view>

          <!-- 视频通话功能 -->
          <view class="more-item" @click="startVideoCall">
            <view class="more-icon-bg video">
              <text class="more-icon">📹</text>
            </view>
            <text class="more-label">视频通话</text>
          </view>

          <!-- 语音通话功能 -->
          <view class="more-item" @click="startVoiceCall">
            <view class="more-icon-bg voice">
              <text class="more-icon">📞</text>
            </view>
            <text class="more-label">语音通话</text>
          </view>

          <!-- 文件功能 -->
          <view class="more-item" @click="chooseFile">
            <view class="more-icon-bg file">
              <text class="more-icon">📁</text>
            </view>
            <text class="more-label">文件</text>
          </view>
        </view>
      </view>


      <!-- 语音录制提示 -->
      <view class="voice-recording-modal" v-if="isRecording" @click="forceResetUI">
        <view class="recording-content">
          <view class="recording-icon">
            <text class="mic-icon">🎤</text>
          </view>
          <text class="recording-text">正在录音...</text>
          <text class="recording-tip">{{ cancelVoice ? '松开取消' : '松开发送，上滑取消' }}</text>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
import { mapState } from 'vuex'
import config from '@/config.js'
import ws from '@/utils/ws.js'
import upload from '@/utils/upload'

export default {
  data() {
    return {
      // 用户信息
      currentRole: 'patient', // 当前用户角色：patient/doctor
      patientName: '',
      doctorAvatar: '',
      doctorId: null,
      doctorName: '',
      sessionId: '',
      // 消息相关
      inputMessage: '',
      messages: [],
      // 录音相关
      isRecording: false,
      recordTime: 0,
      recordTimer: null,
      recordTempFilePath: '',
      recordStartTime: 0,
      // 语音播放相关
      audioContext: null,
      currentVoiceId: null,
      isPlaying: false,
      // 面板控制
      showEmojiPanel: false,
      showMorePanel: false,
      // 图片选择
      selectedImages: [],
      // WebSocket
      // 语音播放器
      audio: null,
      currentAudioId: null,
      // 录音管理器
      recorderManager: null,
      // 面板高度
      emojiPanelHeight: 0,
      morePanelHeight: 0,
      // 滚动位置
      scrollTop: 0,
      // 锚点滚动到底部
      toView: '',
      // 底部安全区（rpx），用于避免被输入区+安全区遮挡
      safeBottomRpx: 0,
      // 键盘高度（rpx），根据 onKeyboardHeightChange 动态计算
      keyboardHeightRpx: 0,
      // 底部输入区的基础高度（rpx），用于为内容区预留等高空间
      inputBaseHeightRpx: 102,
      // H5 端用于估算键盘高度的初始窗口高度（px）
      initialWinHeightPx: 0,
      // 是否正在滚动
      isScrolling: false,
      // 是否显示滚动到底部按钮
      showScrollToBottom: false,
      // 是否显示加载更多
      loadingMore: false,
      // 是否还有更多消息
      hasMore: false,
      // 分页相关
      pageNum: 1,
      pageSize: 20,
      // 是否正在发送消息
      isSending: false,
      // 输入模式：语音或文本
      isVoiceMode: false,
      // 录音取消标记与起始触点
      cancelVoice: false,
      recordStartY: 0,
      // 语音播放控制
      innerAudio: null,
      playingId: '',
      voiceCache: {},
      voiceTapGuardTs: 0,
      // 录音器与H5回退
      recorder: null,
      mediaRecorder: null,
      mediaStream: null,
      audioChunks: [],
      // 表情列表
      emojiList: ['😀','😁','😂','🤣','😊','😍','😘','😜','🤔','😎','😭','😡','👍','👎','🙏','👏','💪','🎉','🌟','❤️'],
      // 自动回复配置
      autoReplies: [
        '您好，我已收到您的消息。',
        '请详细描述一下您的症状和持续时间。',
        '是否伴随发热、咳嗽或其他不适？',
        '稍后我将根据您的情况给出建议。'
      ],
      autoReplyCursor: 0,
      autoReplyCooldown: 600,
      lastAutoReplyTs: 0,
      autoReplyBatch: 1,
      // 单问单答问诊流程（医生侧回复脚本）
      qaFlowReplies: [
        '您好，我是平台医生，请您先描述主要症状、持续时间、伴随症状（如发热/咳嗽等）、既往史以及是否正在用药。',
        '为便于判断，建议补充：体温范围、疼痛部位与程度、是否接触过感冒患者、是否对药物过敏等信息。',
        '根据您的描述，初步建议多喝水、注意休息，清淡饮食；如症状加重或持续不缓解请及时就医。',
        '如需用药，可考虑常见对症药物（示例）：对乙酰氨基酚退热、氨溴索化痰；请严格按照说明书或医嘱用药。',
        '本次咨询暂告一段落，如症状变化或加重请及时就医，祝您早日康复。'
      ],
      // -1 表示流程未开始；调用 startQaFlow 后置为 0
      qaFlowIndex: -1,
      // 流程结束与动作卡片显示
      flowEnded: false,
      endActionShown: false,
      // 科室/病种问诊模板
      qaFlowTemplates: {
        default: [
          '您好，我是平台医生，请您先描述主要症状、持续时间、伴随症状（如发热/咳嗽等）、既往史以及是否正在用药。',
          '为便于判断，建议补充：体温范围、疼痛部位与程度、是否接触过感冒患者、是否对药物过敏等信息。',
          '根据您的描述，初步建议多喝水、注意休息，清淡饮食；如症状加重或持续不缓解请及时就医。',
          '如需用药，可考虑常见对症药物（示例）：对乙酰氨基酚退热、氨溴索化痰；请严格按照说明书或医嘱用药。',
          '本次咨询暂告一段落，如症状变化或加重请及时就医，祝您早日康复。'
        ],
        resp: [
          '请描述呼吸道相关症状：咳嗽/咳痰/鼻塞/流涕/发热及持续时间，有无接触感冒患者。',
          '补充：体温峰值、痰色、是否胸闷气短、既往哮喘/过敏史，是否抽烟。',
          '建议多喝水、休息，合理通风；如高热不退或气促加重，应尽快就医。',
          '对症药示例：对乙酰氨基酚退热、氨溴索化痰、布洛芬止痛；请按说明或医嘱使用。',
          '本次咨询暂告一段落，如症状加重或持续不缓解请及时就医。'
        ],
        gastro: [
          '请描述腹痛/腹泻/恶心呕吐等消化道症状及起始时间，饮食情况与可疑不洁食物。',
          '补充：大便性状与次数、有无发热与脱水、腹痛部位及按压痛、既往胃肠病史。',
          '建议补液，清淡饮食，避免油腻辛辣；警惕明显脱水、血便等情况。',
          '对症药示例：口服补液盐、思密达、蒙脱石散；遵医嘱使用并注意说明书。',
          '若症状未缓解或出现血便/持续高热，请及时就医。'
        ],
        derma: [
          '请描述皮疹/瘙痒的部位、范围、颜色与形态，是否接触过可疑过敏原或新用品。',
          '补充：是否伴发热、渗出、破溃，近期是否更换洗护/衣物/药物，既往过敏史。',
          '建议保持皮肤清洁干燥，避免搔抓与刺激，必要时冷敷缓解瘙痒。',
          '对症药示例：外用炉甘石、弱效激素短期点涂；口服抗组胺药缓解瘙痒。',
          '如皮疹范围扩展或出现感染迹象，请及时就医。'
        ],
        pediatric: [
          '请描述孩子的主要症状、体温、精神与食欲情况，近期是否有感染接触史。',
          '补充：最高体温及持续时间、咳嗽/流涕/腹泻等伴随症状、是否按时接种疫苗。',
          '建议足量饮水与休息，注意物理降温与室内通风，观察精神反应与尿量。',
          '对症药需按体重计量，切勿超量与混用退烧药；如精神差或高热不退尽快就医。',
          '如出现抽搐、呼吸困难、持续呕吐等情况，请立即就医。'
        ]
      }
    }
  },
  computed: {
    // 面板高度
    panelHeight() {
      if (this.showEmojiPanel) return 350
      if (this.showMorePanel) return 200
      return 0
    },
    // 聊天内容底部距离（输入区实际高度 + 面板高度 + 键盘高度 + 底部安全区）
    contentBottom() {
      return (this.inputBaseHeightRpx || 102) + this.panelHeight + (this.keyboardHeightRpx || 0) + (this.safeBottomRpx || 0)
    },
    // 患者头像
    meAvatarFinal() {
      const avatar = (this.$store && this.$store.getters && this.$store.getters.avatar) || ''
      if (avatar) {
        // #ifdef MP-WEIXIN
        const sep = avatar.indexOf('?') > -1 ? '&' : '?'
        return `${avatar}${sep}t=${Date.now()}`
        // #endif
        return avatar
      }
      return '/static/dayi.png'
    },
    // 医生头像
    doctorAvatarFinal() {
      return this.doctorAvatar || '/static/dayi.png'
    },
    // 是否可发送
    canSend() {
      return (this.inputMessage && this.inputMessage.trim().length > 0) || (this.selectedImages && this.selectedImages.length > 0)
    }
  },
  onLoad(options) {
    console.log('聊天页面参数:', options)
    
    // 设置角色和医生头像
    if (options.role) {
      this.currentRole = options.role
    }
    
    if (options.doctorAvatar) {
      this.doctorAvatar = decodeURIComponent(options.doctorAvatar)
    }
    if (options.doctorId) {
      const idNum = Number(options.doctorId)
      this.doctorId = isNaN(idNum) ? null : idNum
    }
    if (options.doctorName) {
      this.doctorName = decodeURIComponent(options.doctorName)
    }
    
    if (options.sessionId) {
      this.sessionId = decodeURIComponent(options.sessionId)
    }
    
    if (options.patientName) {
      this.patientName = decodeURIComponent(options.patientName)
    }
    
    // 计算底部安全区高度（px -> rpx）
    try {
      const sys = typeof uni.getSystemInfoSync === 'function' ? uni.getSystemInfoSync() : null
      if (sys) {
        let safeBottomPx = 0
        if (sys.safeAreaInsets && typeof sys.safeAreaInsets.bottom === 'number') {
          safeBottomPx = sys.safeAreaInsets.bottom
        } else if (sys.safeArea && typeof sys.safeArea.bottom === 'number') {
          const screenH = sys.screenHeight || 0
          safeBottomPx = screenH > 0 ? (screenH - sys.safeArea.bottom) : 0
        }
        const ratio = sys.windowWidth ? (750 / sys.windowWidth) : 2
        this.safeBottomRpx = Math.max(0, Math.round(safeBottomPx * ratio))
      }
    } catch (e) {}
    
    // 初始化录音
    this.initRecorder()
    
    // 初始化音频上下文（H5和小程序）
    if (typeof window !== 'undefined') {
      try {
        this.audioContext = uni.createInnerAudioContext()
      } catch (e) {
        console.warn('创建音频上下文失败', e)
      }
    }
    
    // 依据路由参数设置问诊模板
    const flowKeyRaw = (options.flow || options.dept || options.disease || '').toString()
    if (flowKeyRaw) {
      this.setQaFlowByKey(flowKeyRaw)
    }
    // 启动单问单答流程：医生先问需求
    this.startQaFlow()
    
    // 监听键盘高度变化，动态抬升输入区与内容区域底部
    uni.onKeyboardHeightChange((res) => {
      try {
        const hPx = (res && res.height) || 0
        const sys = typeof uni.getSystemInfoSync === 'function' ? uni.getSystemInfoSync() : null
        const ratio = sys && sys.windowWidth ? (750 / sys.windowWidth) : 2
        this.keyboardHeightRpx = Math.max(0, Math.round(hPx * ratio))
        if (hPx > 0) {
          // 键盘弹出时收起表情和更多面板，并滚动到底部
          this.showEmojiPanel = false
          this.showMorePanel = false
          this.$nextTick(() => this.scrollToBottom())
        }
      } catch (e) {
        this.keyboardHeightRpx = 0
      }
    })
    
    // 确保页面布局正确
    this.$nextTick(() => {
      this.updateInputBaseHeight(() => {
        this.scrollToBottom()
      })
    })

    // #ifdef H5
    try {
      this.initialWinHeightPx = window.innerHeight || document.documentElement.clientHeight || 0
      this._onResize = this.handleH5Resize
      window.addEventListener('resize', this._onResize, { passive: true })
    } catch (e) {}
    // #endif
    
    // 初始化 WebSocket 连接
    this.initWebSocket()
  },
  onUnload() {
    // #ifdef H5
    try {
      this._onResize && window.removeEventListener('resize', this._onResize)
    } catch (e) {}
    // #endif
    try {
      if (this.innerAudio) {
        this.innerAudio.stop && this.innerAudio.stop()
        this.innerAudio.destroy && this.innerAudio.destroy()
        this.innerAudio = null
      }
      this.playingId = ''
      this.isPlaying = false
      this.isRecording = false
      this.cancelVoice = false
    } catch (e) {}
    try {
      this._onWsOpen && uni.$off('global-ws-open', this._onWsOpen)
      this._onWsMsg && uni.$off('global-ws-message', this._onWsMsg)
      this._onWsClose && uni.$off('global-ws-close', this._onWsClose)
    } catch (e) {}
  },
  watch: {
    showEmojiPanel() {
      this.$nextTick(() => {
        this.updateInputBaseHeight(() => this.scrollToBottom())
      })
    },
    showMorePanel() {
      this.$nextTick(() => {
        this.updateInputBaseHeight(() => this.scrollToBottom())
      })
    },
    selectedImages() {
      this.$nextTick(() => {
        this.updateInputBaseHeight(() => this.scrollToBottom())
      })
    }
  },
  methods: {
    // H5 端：通过窗口高度变化估算键盘高度
    handleH5Resize() {
      try {
        const hNow = window.innerHeight || document.documentElement.clientHeight || 0
        const delta = Math.max(0, (this.initialWinHeightPx || 0) - hNow)
        if (delta > 60) { // 认为键盘弹出
          const sys = typeof uni.getSystemInfoSync === 'function' ? uni.getSystemInfoSync() : null
          const ratio = sys && sys.windowWidth ? (750 / sys.windowWidth) : 2
          this.keyboardHeightRpx = Math.max(0, Math.round(delta * ratio))
          this.$nextTick(() => this.scrollToBottom())
        } else {
          this.keyboardHeightRpx = 0
        }
      } catch (e) {}
    },
    onInputFocus() {
      // 强制滚底部，避免看不到输入位置
      this.$nextTick(() => this.scrollToBottom())
    },
    onInputBlur() {
      // 收起键盘后清零键盘高度
      // #ifdef H5
      this.keyboardHeightRpx = 0
      // #endif
    },
    // 动态测量输入区域实际高度（px -> rpx），用于给内容区预留空间
    updateInputBaseHeight(cb) {
      try {
        const q = uni.createSelectorQuery().in(this)
        q.select('.input-area').boundingClientRect(rect => {
          if (rect && rect.height) {
            const sys = typeof uni.getSystemInfoSync === 'function' ? uni.getSystemInfoSync() : null
            const ratio = sys && sys.windowWidth ? (750 / sys.windowWidth) : 2
            this.inputBaseHeightRpx = Math.max(80, Math.round(rect.height * ratio))
          }
          typeof cb === 'function' && cb()
        }).exec()
      } catch (e) {
        typeof cb === 'function' && cb()
      }
    },
    // 根据流程键设置问诊模板
    setQaFlowByKey(key) {
      const templates = this.qaFlowTemplates
      if (templates[key]) {
        this.qaFlowReplies = [...templates[key]]
      } else {
        this.qaFlowReplies = [...templates.default]
      }
    },
    // 重新开始本次问诊：重置状态并从首条脚本开始
    restartConsult() {
      try {
        this.flowEnded = false
        this.endActionShown = false
        this.lastAutoReplyTs = 0
        this.qaFlowIndex = -1
        // 可选：追加系统提示
        // this.addMessage('已重新开始本次问诊', 'system', 'text')
        this.startQaFlow()
        this.$nextTick(() => this.scrollToBottom())
      } catch (e) {
        console.error('重新开始问诊失败:', e)
      }
    },
    // 跳转到医生评价页
    goToComment() {
      if (!this.doctorId) {
        uni.showToast({ title: '缺少医生信息，无法评价', icon: 'none' })
        return
      }
      const params = [
        `doctorId=${this.doctorId}`,
        `doctorName=${encodeURIComponent(this.doctorName || '')}`,
        `doctorAvatar=${encodeURIComponent(this.doctorAvatar || '')}`
      ].join('&')
      uni.navigateTo({ url: `/pages/consult/evaluate?${params}` })
    },
    // 发送消息（模拟）- 单问单答流程触发下一条医生回复
    sendToWebSocket(message) {
      console.log('发送消息:', message)
      const opposite = this.currentRole === 'patient' ? 'doctor' : 'patient'
      const now = Date.now()
      // 防抖：一次用户操作（文本或图文组合）只触发一次自动回复
      if (now - this.lastAutoReplyTs < this.autoReplyCooldown) {
        return
      }
      this.lastAutoReplyTs = now

      // 若流程尚未开始，先触发起始问询
      if (this.qaFlowIndex < 0) {
        this.startQaFlow()
        return
      }
      // 已到达结尾则不再自动回复
      if (this.qaFlowIndex >= this.qaFlowReplies.length - 1) {
        return
      }

      const nextIdx = this.qaFlowIndex + 1
      const reply = this.qaFlowReplies[nextIdx] || '好的'
      const delay = 600 + Math.floor(Math.random() * 400)
      setTimeout(() => {
        this.addMessage(reply, opposite, 'text')
        if (nextIdx === this.qaFlowReplies.length - 1) {
          // 已到流程末尾，展示操作区
          this.flowEnded = true
          if (!this.endActionShown) {
            setTimeout(() => {
              this.addMessage('本次咨询已结束', 'system', 'action')
              this.endActionShown = true
            }, 300)
          }
        }
      }, delay)
      this.qaFlowIndex = nextIdx
    },
    // 通过 WebSocket 发送聊天消息
    sendWsChat(type, content, duration = 0) {
      try {
        if (!ws || !ws.isConnected || !ws.isConnected()) return
        const payload = {
          id: `msg_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,
          type: (type === 'text' || type === 'image' || type === 'voice') ? type : 'text',
          roomId: this.sessionId || '',
          sessionId: this.sessionId || '',
          doctorId: this.doctorId || '',
          from: this.currentRole || 'patient',
          content: type === 'text' ? (content || '') : '',
          url: type !== 'text' ? (content || '') : '',
          duration: duration || 0,
          timestamp: new Date().toISOString()
        }
        ws.send(payload)
      } catch (e) {
        console.warn('sendWsChat failed', e)
      }
    },
    // 初始化 WebSocket
    initWebSocket() {
      try {
        const base = (config && config.signalBaseUrl) || ''
        if (!base) {
          console.warn('缺少 signalBaseUrl，跳过 WebSocket 连接')
          return
        }
        const uid = (this.$store && this.$store.getters && this.$store.getters.id) || ''
        const sid = (this.sessionId && String(this.sessionId)) || ''
        const rid = sid || ((this.doctorId != null && uid) ? `room_${this.doctorId}_${uid}` : '')
        if (!rid) {
          console.warn('缺少 roomId，跳过 WebSocket 连接')
          return
        }
        let url = `${base}?roomId=${encodeURIComponent(rid)}`
        const token = (this.$store && this.$store.getters && this.$store.getters.token) || ''
        if (token) url += `&token=${encodeURIComponent(token)}`
        if (this.doctorId != null) url += `&doctorId=${encodeURIComponent(this.doctorId)}`
        if (uid) url += `&userId=${encodeURIComponent(uid)}`

        // 连接全局 WS
        ws.connect(url)

        // 事件处理（仅当前页面实例）
        this._onWsOpen = () => {
          console.log('[WS] open:', url)
        }
        this._onWsMsg = (evt) => {
          try {
            const raw = evt && evt.data
            const msg = typeof raw === 'string' ? JSON.parse(raw) : raw
            if (!msg) return
            const t = msg.type || msg.msgType || 'text'
            const senderRaw = msg.from || msg.sender || 'doctor'
            const uiSender = senderRaw === 'patient' ? 'patient' : 'doctor'
            if (t === 'text' || t === 'chat') {
              const content = msg.content || msg.text || ''
              if (content) this.addMessage(content, uiSender, 'text')
            } else if (t === 'image') {
              const u = msg.url || msg.src || msg.extra || ''
              if (u) this.addMessage('', uiSender, 'image', u)
            } else if (t === 'voice') {
              const u = msg.url || msg.src || msg.extra || ''
              const d = parseInt(msg.duration || 0, 10) || 0
              if (u) this.addMessage('', uiSender, 'voice', u, d)
            } else if (t === 'rtc_invite') {
              // 可选：处理来电邀请
              console.log('[WS] rtc invite:', msg)
            }
          } catch (e) {
            console.warn('WS message parse error:', e)
          }
        }
        this._onWsClose = () => {
          console.log('[WS] close')
        }
        try {
          uni.$off('global-ws-open', this._onWsOpen)
          uni.$off('global-ws-message', this._onWsMsg)
          uni.$off('global-ws-close', this._onWsClose)
        } catch (e) {}
        uni.$on('global-ws-open', this._onWsOpen)
        uni.$on('global-ws-message', this._onWsMsg)
        uni.$on('global-ws-close', this._onWsClose)
      } catch (e) {
        console.warn('initWebSocket error', e)
      }
    },
    
    // 启动单问单答流程：医生先问需求
    startQaFlow() {
      try {
        this.flowEnded = false
        this.endActionShown = false
        const first = (this.qaFlowReplies && this.qaFlowReplies[0]) || '您好，请问有什么可以帮到您？'
        this.addMessage(first, 'doctor', 'text')
        this.qaFlowIndex = 0
      } catch (e) {
        this.flowEnded = false
        this.endActionShown = false
        this.addMessage('您好，请问有什么可以帮到您？', 'doctor', 'text')
        this.qaFlowIndex = 0
      }
    },
    // 切换语音/文本输入模式
    toggleVoiceMode() {
      this.isVoiceMode = !this.isVoiceMode
      this.showEmojiPanel = false
      this.showMorePanel = false
    },
    // 发送消息：文本与已选图片（上传后发送，仅触发一次自动回复）
    async sendMessage() {
      if (!this.canSend) return
      const text = (this.inputMessage || '').trim()
      let triggered = false
      if (text) {
        this.addMessage(text, this.currentRole, 'text')
        this.sendWsChat('text', text)
        triggered = true
      }
      if (this.selectedImages && this.selectedImages.length > 0) {
        const imgs = [...this.selectedImages]
        this.selectedImages = []
        triggered = true
        // 逐张异步上传，上传成功后替换本地预览URL并通过WS发送
        for (const img of imgs) {
          const msgRef = this.addMessage('', this.currentRole, 'image', img)
          try {
            const res = await upload({ url: '/file/upload', filePath: img, name: 'file' })
            const url = (res && res.data && res.data.url) || ''
            if (url) {
              msgRef.extra = url
              this.sendWsChat('image', url)
            } else {
              uni.showToast({ title: '图片上传失败', icon: 'none' })
            }
          } catch (e) {
            uni.showToast({ title: '图片上传失败', icon: 'none' })
          }
        }
      }
      this.inputMessage = ''
      if (triggered) {
        this.sendToWebSocket({ type: 'user_message' })
      }
      this.$nextTick(() => this.updateInputBaseHeight(() => this.scrollToBottom()))
    },
    // 滚动到底部
    scrollToBottom() {
      // 让 scroll-view 滚到底部
      this.scrollTop = 1e8 + (this.messages ? this.messages.length : 0)
      const last = this.messages && this.messages[this.messages.length - 1]
      if (last && last.id) {
        this.toView = 'm_' + last.id
      }
    },
    // 添加消息
    addMessage(content, sender, type = 'text', extra = '', duration = 0) {
      const mid = this.genId()
      const message = {
        id: mid,
        content,
        sender,
        type,
        extra,
        duration,
        timestamp: new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
      }
      this.messages.push(message)
      this.$nextTick(() => {
        this.toView = 'm_' + mid
        // 再次异步，确保 scroll-into-view 在部分端（H5/APP）生效
        setTimeout(() => this.updateInputBaseHeight(() => this.scrollToBottom()), 0)
      })
      // 返回以便后续更新（如：上传成功后替换URL）
      return message
    },
    // 返回上一页
    goBack() {
      uni.navigateBack()
    },
    // 生成消息ID
    genId() {
      return `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
    },

    // 是否显示时间（首条或与上一条间隔>=5分钟）
    shouldShowTime(index) {
      if (index === 0) return true;
      const prev = this.messages[index - 1]
      const curr = this.messages[index]
      // 时间是字符串，这里简化处理：每10条展示一次
      return (index % 10) === 0 || prev.timestamp !== curr.timestamp
    },

    // 预览图片
    previewImage(url) {
      if (!url) return
      uni.previewImage({
        current: url,
        urls: this.messages.filter(m => m.type === 'image').map(m => m.extra)
      })
    },

    // 播放语音
    async playVoice(src, id) {
      if (!src) return
      // 防止 tap/click 双触发导致立刻停止
      const now = Date.now()
      if (now - this.voiceTapGuardTs < 300) {
        return
      }
      this.voiceTapGuardTs = now
      try {
        // 停止当前播放
        if (!this.innerAudio) {
          this.innerAudio = uni.createInnerAudioContext()
          // 音频基础配置（避免静音开关影响、提高可听度）
          if (typeof this.innerAudio.obeyMuteSwitch !== 'undefined') {
            this.innerAudio.obeyMuteSwitch = false
          }
          if (typeof this.innerAudio.autoplay !== 'undefined') {
            this.innerAudio.autoplay = false
          }
          if (typeof this.innerAudio.volume !== 'undefined') {
            this.innerAudio.volume = 1
          }
          // 事件日志，便于定位问题
          this.innerAudio.onPlay && this.innerAudio.onPlay(() => {
            this.isPlaying = true
            console.log('Audio play start:', this.innerAudio.src)
          })
          this.innerAudio.onCanplay && this.innerAudio.onCanplay(() => {
            console.log('Audio canplay')
          })
          this.innerAudio.onEnded(() => {
            this.playingId = ''
            this.isPlaying = false
            // 兜底：播放结束时也确保没有录音遮罩残留
            this.isRecording = false
            this.cancelVoice = false
          })
          this.innerAudio.onStop && this.innerAudio.onStop(() => {
            this.playingId = ''
            this.isPlaying = false
          })
        }
        // 若已有音频在播放，先尝试停止
        try { this.innerAudio && this.innerAudio.stop && this.innerAudio.stop() } catch (e) {}
        const realSrc = await this.resolvePlayableSrc(src)
        this.innerAudio.src = realSrc
        this.playingId = id || ''
        this.innerAudio.play()
      } catch (e) {
        console.error('播放失败', e)
        uni.showToast({ title: '无法播放语音', icon: 'none' })
      }
    },

    // 解析可播放的音频地址：
    // - H5: 直接返回 http/blob 均可
    // - App/小程序: http 先下载到本地临时文件后再播放（更稳定）
    resolvePlayableSrc(src) {
      try {
        const isHttp = /^https?:\/\//i.test(src)
        const isBlob = /^blob:/i.test(src)
        // H5 平台 blob/http 直接返回
        // #ifdef H5
        return Promise.resolve(src)
        // #endif
        // 非 H5 平台：优先使用本地路径或下载后的临时文件
        if (!isHttp) return Promise.resolve(src)
        if (this.voiceCache[src]) return Promise.resolve(this.voiceCache[src])
        return new Promise((resolve) => {
          uni.downloadFile({
            url: src,
            success: (res) => {
              const path = res.tempFilePath || res.filePath || ''
              if (path) {
                this.voiceCache[src] = path
                resolve(path)
              } else {
                resolve(src)
              }
            },
            fail: (err) => {
              console.error('下载语音失败', err)
              resolve(src)
            }
          })
        })
      } catch (e) {
        return Promise.resolve(src)
      }
    },

    // 初始化录音能力
    initRecorder() {
      try {
        if (typeof uni.getRecorderManager === 'function') {
          this.recorder = uni.getRecorderManager()
          // 监听停止
          this.recorder.onStop && this.recorder.onStop(async (res) => {
            const tempFilePath = res.tempFilePath || (res.tempFile && res.tempFile.path) || ''
            const duration = res.duration ? Math.round(res.duration / 1000) : Math.max(1, Math.round((Date.now() - this.recordStartTime) / 1000))
            if (!this.cancelVoice && tempFilePath) {
              const msgRef = this.addMessage('', this.currentRole, 'voice', tempFilePath, duration)
              try {
                const up = await upload({ url: '/file/upload', filePath: tempFilePath, name: 'file' })
                const url = (up && up.data && up.data.url) || ''
                if (url) {
                  msgRef.extra = url
                  this.sendWsChat('voice', url, duration)
                } else {
                  uni.showToast({ title: '语音上传失败', icon: 'none' })
                }
              } catch (err) {
                uni.showToast({ title: '语音上传失败', icon: 'none' })
              }
            }
            this.isRecording = false
            this.cancelVoice = false
          })
          this.recorder.onError && this.recorder.onError((err) => {
            console.error('录音错误', err)
            this.isRecording = false
            uni.showToast({ title: '录音失败', icon: 'none' })
          })
        }
      } catch (e) {
        console.warn('初始化原生录音失败，H5将用MediaRecorder回退', e)
      }
    },

    // 语音录制
    async startRecording(e) {
      try {
        this.isRecording = true
        this.cancelVoice = false
        this.recordStartY = e && e.touches && e.touches[0] ? e.touches[0].clientY : 0
        this.recordStartTime = Date.now()

        // 原生/小程序优先
        if (this.recorder) {
          this.recorder.start && this.recorder.start({
            duration: 60000,
            sampleRate: 16000,
            numberOfChannels: 1,
            format: 'mp3',
            encodeBitRate: 96000
          })
          return
        }

        // H5 回退
        const hasNav = typeof navigator !== 'undefined' && navigator.mediaDevices
        const hasMR = typeof window !== 'undefined' && window.MediaRecorder
        if (!hasNav || !hasMR) {
          uni.showToast({ title: '当前环境不支持录音', icon: 'none' })
          this.isRecording = false
          return
        }
        const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
        this.mediaStream = stream
        this.audioChunks = []
        // 选择最合适的音频编码格式（提升跨浏览器可播放性）
        let mime = ''
        try {
          if (typeof MediaRecorder !== 'undefined' && typeof MediaRecorder.isTypeSupported === 'function') {
            if (MediaRecorder.isTypeSupported('audio/webm;codecs=opus')) mime = 'audio/webm;codecs=opus'
            else if (MediaRecorder.isTypeSupported('audio/webm')) mime = 'audio/webm'
            else if (MediaRecorder.isTypeSupported('audio/mp4')) mime = 'audio/mp4'
            else if (MediaRecorder.isTypeSupported('audio/aac')) mime = 'audio/aac'
          }
        } catch (e) {
          console.warn('检测 MediaRecorder 支持失败', e)
        }
        const mr = mime ? new MediaRecorder(stream, { mimeType: mime }) : new MediaRecorder(stream)
        this.mediaRecorder = mr
        mr.ondataavailable = (evt) => {
          if (evt.data && evt.data.size > 0) this.audioChunks.push(evt.data)
        }
        mr.onstop = async () => {
          const blobType = mime || 'audio/webm'
          const blob = new Blob(this.audioChunks, { type: blobType })
          const localUrl = URL.createObjectURL(blob)
          const duration = Math.max(1, Math.round((Date.now() - this.recordStartTime) / 1000))
          if (!this.cancelVoice) {
            const msgRef = this.addMessage('', this.currentRole, 'voice', localUrl, duration)
            try {
              // 推断扩展名
              let ext = 'webm'
              if (/mp4/i.test(blobType)) ext = 'mp4'
              else if (/aac/i.test(blobType)) ext = 'aac'
              else if (/mpeg|mp3/i.test(blobType)) ext = 'mp3'
              const file = new File([blob], `voice_${Date.now()}.${ext}`, { type: blobType })
              const up = await upload({ url: '/file/upload', file, name: 'file' })
              const url = (up && up.data && up.data.url) || ''
              if (url) {
                msgRef.extra = url
                this.sendWsChat('voice', url, duration)
                try { URL.revokeObjectURL(localUrl) } catch (e) {}
              } else {
                uni.showToast({ title: '语音上传失败', icon: 'none' })
              }
            } catch (e) {
              uni.showToast({ title: '语音上传失败', icon: 'none' })
            }
          } else {
            URL.revokeObjectURL(localUrl)
          }
          // 释放麦克风
          this.mediaStream && this.mediaStream.getTracks().forEach(t => t.stop())
          this.mediaStream = null
          this.isRecording = false
          this.cancelVoice = false
        }
        mr.start()
      } catch (err) {
        console.error('开始录音失败', err)
        this.isRecording = false
        uni.showToast({ title: '无法开始录音', icon: 'none' })
      }
    },

    onRecordTouchMove(e) {
      if (!this.isRecording) return
      const y = e && e.touches && e.touches[0] ? e.touches[0].clientY : 0
      const delta = y - this.recordStartY
      // 上滑超过40px则取消
      this.cancelVoice = delta < -40
    },

    stopRecording() {
      if (!this.isRecording) return
      // 若处于取消状态，则走取消逻辑
      if (this.cancelVoice) {
        return this.cancelRecording()
      }
      try {
        if (this.recorder) {
          this.recorder.stop && this.recorder.stop()
        } else if (this.mediaRecorder) {
          if (this.mediaRecorder.state !== 'inactive') this.mediaRecorder.stop()
        }
      } catch (e) {
        console.error('停止录音失败', e)
        this.isRecording = false
      }
      // 安全兜底：若某些端 onStop 未回调，超时强制复位，避免遮罩残留
      setTimeout(() => {
        if (this.isRecording) {
          this.isRecording = false
          this.cancelVoice = false
        }
      }, 1200)
    },

    cancelRecording() {
      if (!this.isRecording) return
      this.cancelVoice = true
      try {
        if (this.recorder) {
          // 停止但不发送
          this.recorder.stop && this.recorder.stop()
        } else if (this.mediaRecorder) {
          if (this.mediaRecorder.state !== 'inactive') this.mediaRecorder.stop()
        }
      } catch (e) {}
      // 其余清理在 onStop/onstop 中完成
      // 安全兜底：防止某些端没有触发 onStop
      setTimeout(() => {
        if (this.isRecording) {
          this.isRecording = false
          this.cancelVoice = false
        }
      }, 1200)
    },

    // 紧急复位：一键清理可能遮挡交互的状态
    forceResetUI() {
      try {
        if (this.innerAudio) {
          this.innerAudio.stop && this.innerAudio.stop()
        }
      } catch (e) {}
      this.playingId = ''
      this.isPlaying = false
      this.isRecording = false
      this.cancelVoice = false
      this.showEmojiPanel = false
      this.showMorePanel = false
    },

    // 语音时长格式化
    formatVoiceDuration(sec) {
      const s = Math.max(1, parseInt(sec || 0, 10))
      return `${s}\"`
    },

    // 切换表情面板
    toggleEmojiPanel() {
      this.showEmojiPanel = !this.showEmojiPanel
      if (this.showEmojiPanel) {
        this.showMorePanel = false
      }
    },

    // 选择表情
    selectEmoji(emoji) {
      this.inputMessage += emoji
      this.showEmojiPanel = false  // 选择表情后关闭表情面板
    },

    // 切换更多功能面板
    toggleMorePanel() {
      this.showMorePanel = !this.showMorePanel
      if (this.showMorePanel) {
        this.showEmojiPanel = false
      }
    },

    // 选择图片
    chooseImage() {
      uni.chooseImage({
        count: 9, // 最多选择9张
        sizeType: ['compressed'],
        sourceType: ['album', 'camera'],
        success: (res) => {
          this.selectedImages = [...this.selectedImages, ...res.tempFilePaths]
          // 限制最多9张图片
          if (this.selectedImages.length > 9) {
            this.selectedImages = this.selectedImages.slice(0, 9)
          }
          uni.showToast({
            title: `已选择${res.tempFilePaths.length}张图片`,
            icon: 'success'
          })
        },
        fail: (err) => {
          console.error('选择图片失败:', err)
          uni.showToast({
            title: '选择图片失败',
            icon: 'none'
          })
        }
      })
      this.showMorePanel = false
    },

    // 移除选中的图片
    removeSelectedImage(index) {
      this.selectedImages.splice(index, 1)
    },

    // 开始视频通话
    startVideoCall() {
      this.showMorePanel = false
      try {
        const roomID = `room_${Date.now()}_${Math.floor(Math.random() * 1000)}`
        const role = 'caller'

        // 通过全局 WebSocket 发送视频邀请，携带 roomId
        try {
          if (ws && ws.isConnected && ws.isConnected()) {
            const payload = {
              id: `invite_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,
              type: 'rtc_invite',
              roomId: roomID,
              role: role,
              from: this.currentRole || 'patient',
              to: 'doctor',
              doctorId: this.doctorId || '',
              patientName: this.patientName || '',
              timestamp: new Date().toISOString()
            }
            ws.send(payload)
          }
        } catch (e) {
          console.warn('发送视频邀请失败(忽略不阻塞跳转):', e)
        }

        // 本端跳转进入视频页
        const params = [`roomId=${encodeURIComponent(roomID)}`, `role=${role}`].join('&')
        uni.navigateTo({ url: `/pages/consult/video?${params}` })
      } catch (error) {
        console.error('视频通话跳转失败:', error)
        uni.showModal({
          title: '跳转失败',
          content: '无法进入视频通话页面，请稍后重试',
          showCancel: false
        })
      }
    },

    // 开始语音通话
    startVoiceCall() {
      uni.showToast({
        title: '正在发起语音通话...',
        icon: 'loading',
        duration: 2000
      })

      setTimeout(() => {
        uni.showModal({
          title: '语音通话',
          content: '语音通话功能开发中，敬请期待！',
          showCancel: false
        })
      }, 2000)

      this.showMorePanel = false
    },

    // 选择文件
    chooseFile() {
      uni.showActionSheet({
        itemList: ['选择文档', '选择音频', '选择其他文件'],
        success: (res) => {
          const fileTypes = ['文档', '音频', '其他文件']
          uni.showToast({
            title: `选择${fileTypes[res.tapIndex]}功能开发中`,
            icon: 'none'
          })
        }
      })

      this.showMorePanel = false
    }
  }
}
</script>

<style scoped>
.chat-container {
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: #f5f5f5;
}

.nav-header {
  height: 88rpx;
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 30rpx;
  border-bottom: 1px solid #e5e5e5;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 99;
}

.chat-content {
  position: fixed;
  top: 88rpx;
  left: 0;
  right: 0;
  bottom: 102rpx; /* 默认底部距离，会被动态调整 */
  padding: 20rpx;
  transition: bottom 0.3s ease; /* 平滑过渡效果 */
  overflow-y: auto;
}

.input-area {
  background: #fff;
  border-top: 1px solid #e5e5e5;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1100;
  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}

.images-preview {
  display: flex;
  flex-wrap: wrap;
  gap: 10rpx;
  padding: 15rpx 20rpx 10rpx 20rpx;
  background: white;
}

.image-preview {
  width: 120rpx;
  height: 120rpx;
  position: relative;
  border-radius: 8rpx;
  overflow: hidden;
}

.preview-img {
  width: 120rpx;
  height: 120rpx;
  border-radius: 8rpx;
  object-fit: cover;
}

.remove-btn {
  position: absolute;
  top: -5rpx;
  right: -5rpx;
  width: 30rpx;
  height: 30rpx;
  background-color: #ff4444;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
}

.remove-icon {
  color: white;
  font-size: 20rpx;
  font-weight: bold;
  line-height: 1;
}

.input-tools {
  display: flex;
  align-items: center;
  padding: 20rpx;
  gap: 12rpx;
  flex-wrap: nowrap;
  min-height: 72rpx;
}

.input-container {
  flex: 1;
  min-width: 0;
}

.emoji-panel {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 350rpx;
  background: #fff;
  border-top: 1px solid #e5e5e5;
  padding: 20rpx;
  box-sizing: border-box;
  z-index: 1000;
}

.more-panel {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 200rpx;
  background: #f8f9fa;
  border-top: 1px solid #e5e5e5;
  padding: 25rpx 20rpx;
  box-sizing: border-box;
  z-index: 1000;
}

.emoji-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 20rpx;
  height: 100%;
  overflow-y: auto;
}

.emoji-item {
  width: 60rpx;
  height: 60rpx;
  border-radius: 8rpx;
}

.emoji-item:active {
  background-color: #f0f0f0;
}

.emoji-text {
  font-size: 40rpx;
}

.more-grid {
  display: flex;
  gap: 40rpx;
  align-items: center;
}

.more-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10rpx;
}

.more-icon-bg {
  width: 80rpx;
  height: 80rpx;
  border-radius: 16rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.more-icon-bg.photo {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  margin-left: 30rpx; /* 增加左边距，向左移动患者头像，与医生头像保持一致 */
}

.more-icon-bg.video {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}

.more-icon-bg.voice {
  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}

.more-icon-bg.file {
  background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}

.more-icon {
  font-size: 36rpx;
  color: #fff;
}

.more-label {
  font-size: 24rpx;
  color: #666;
}

.time-divider {
  text-align: center;
  margin: 40rpx 0;
}

.time-text {
  font-size: 24rpx;
  color: #999;
  background: rgba(0, 0, 0, 0.1);
  padding: 8rpx 20rpx;
  border-radius: 20rpx;
}

.message-item {
  display: flex;
  margin-bottom: 32rpx;
  align-items: flex-start;
  padding: 0 5rpx; /* 减少左右内边距 */
}

.patient-message {
  justify-content: flex-end;
}

.doctor-message {
  justify-content: flex-start;
}

.avatar {
  width: 70rpx; /* 稍微减小头像尺寸 */
  height: 70rpx;
  border-radius: 35rpx;
  margin: 0 10rpx; /* 减少头像边距 */
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  flex-shrink: 0;
}

.patient-avatar {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.doctor-avatar {
  background: #f0f0f0;
}

.avatar-text {
  color: #fff;
  font-size: 26rpx; /* 稍微减小字体 */
  font-weight: bold;
}

.avatar-img {
  width: 100%;
  height: 100%;
  border-radius: 35rpx;
}

.message-bubble {
  max-width: 420rpx; /* 减小最大宽度，确保头像不被挤出屏幕 */
  padding: 20rpx;
  border-radius: 16rpx;
  position: relative;
  word-wrap: break-word;
}

.patient-bubble {
  background: #007AFF;
  color: #fff;
}

.doctor-bubble {
  background: #fff;
  color: #333;
  border: 1px solid #e5e5e5;
}

.message-text {
  font-size: 30rpx;
  line-height: 1.4;
}

/* 新增：消息内图片与语音样式 */
.message-image {
  max-width: 400rpx;
  border-radius: 8rpx;
}

.voice-message {
  display: inline-flex;
  align-items: center;
  gap: 12rpx;
}
.voice-icon { font-size: 32rpx; }
.voice-duration { font-size: 26rpx; color: #666; }

.input-area {
  background: #fff;
  border-top: 1px solid #e5e5e5;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1100;
  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}

.images-preview {
  display: flex;
  flex-wrap: wrap;
  gap: 10rpx;
  padding: 20rpx;
  background: white;
  border-top: 1rpx solid #eee;
}

.input-tools {
  display: flex;
  align-items: center;
  padding: 20rpx;
  background: white;
  border-top: 1rpx solid #eee;
  gap: 12rpx;
  flex-wrap: nowrap;
  min-height: 72rpx;
}

.input-container {
  flex: 1;
  min-width: 0;
}

.emoji-panel {
  width: 100%;
  height: 350rpx;
  background: #fff;
  border-top: 1px solid #e5e5e5;
  padding: 20rpx;
  box-sizing: border-box;
}

.emoji-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 15rpx;
}

.emoji-item {
  width: 70rpx;
  height: 70rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12rpx;
  transition: all 0.2s;
}

.emoji-item:active {
  background: #f0f0f0;
  transform: scale(0.9);
}

.emoji-text {
  font-size: 40rpx;
}

.more-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 30rpx;
  justify-content: flex-start;
}

.more-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 110rpx;
}

.more-icon-bg {
  width: 90rpx;
  height: 90rpx;
  border-radius: 18rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 12rpx;
  transition: all 0.2s;
}

.more-icon-bg.photo {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.more-icon-bg.video {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}

.more-icon-bg.voice {
  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}

.more-icon-bg.file {
  background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}

.more-icon-bg:active {
  transform: scale(0.9);
}

.more-icon {
  font-size: 36rpx;
  color: #fff;
}

.more-label {
  font-size: 22rpx;
  color: #666;
  text-align: center;
}

/* 语音录制模态框 */
.voice-recording-modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
}

.recording-content {
  background: #fff;
  padding: 50rpx;
  border-radius: 20rpx;
  text-align: center;
  min-width: 280rpx;
}

.recording-icon {
  margin-bottom: 20rpx;
}

.mic-icon {
  font-size: 70rpx;
  color: #ff4757;
  animation: pulse 1s infinite;
}

.recording-text {
  font-size: 30rpx;
  color: #333;
  margin-bottom: 10rpx;
  display: block;
}

.recording-tip {
  font-size: 22rpx;
  color: #999;
  display: block;
}

.images-preview {
  display: flex;
  flex-wrap: wrap;
  gap: 10rpx;
  margin-bottom: 10rpx;
}

.image-preview {
  width: 120rpx;
  height: 120rpx;
  display: inline-block;
  overflow: hidden;
  position: relative;
  margin-right: 10rpx;
  margin-bottom: 10rpx;
}

.preview-img {
  width: 120rpx !important;
  height: 120rpx !important;
  border-radius: 8rpx;
  object-fit: cover;
}

.remove-btn {
  position: absolute;
  top: -5rpx;
  right: -5rpx;
  width: 30rpx;
  height: 30rpx;
  background-color: #ff4444;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
}

.remove-icon {
  color: white;
  font-size: 20rpx;
  font-weight: bold;
  line-height: 1;
}

.tool-btn {
  width: 72rpx;
  height: 72rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f8f9fa;
  border-radius: 36rpx;
  transition: all 0.2s;
  flex-shrink: 0;
}

.tool-btn:active {
  background: #e9ecef;
  transform: scale(0.95);
}

.tool-icon {
  font-size: 32rpx;
  transition: all 0.3s ease;
}

.tool-icon.recording {
  color: #ff4757;
  animation: pulse 1s infinite;
}

.tool-icon.rotated {
  transform: rotate(45deg);
}

.message-input {
  width: 100%;
  height: 72rpx;
  padding: 0 20rpx;
  background: #f8f9fa;
  border-radius: 36rpx;
  font-size: 30rpx;
  border: none;
  outline: none;
}

.send-btn {
  height: 72rpx;
  padding: 0 24rpx;
  background: #ccc;
  border-radius: 36rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s;
  flex-shrink: 0;
}

.send-btn.active {
  background: #007AFF;
}

.send-btn.active:active {
  background: #0056CC;
  transform: scale(0.95);
}

.send-text {
  font-size: 28rpx;
  color: #fff;
  font-weight: 500;
}

.press-to-talk-btn {
  width: 120rpx;
  height: 72rpx;
  background: #ff4757;
  border-radius: 36rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s;
  flex-shrink: 0;
}

.press-to-talk-btn:active {
  background: #ff2a2a;
  transform: scale(0.95);
}

.press-to-talk-icon {
  font-size: 32rpx;
  color: #fff;
  animation: pulse 1s infinite;
}

.press-to-talk {
  height: 72rpx;
  background: #f8f9fa;
  border-radius: 36rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s;
  border: 1rpx solid #e5e5e5;
}

.press-to-talk:active {
  background: #eceff3;
}

.press-text {
  font-size: 28rpx;
  color: #333;
}

@keyframes pulse {
  0% { transform: scale(1); }
  50% { transform: scale(1.1); }
  100% { transform: scale(1); }
}
</style>
